package kz.asoft.am;

import de.congrace.exp4j.Calculable;
import de.congrace.exp4j.ExpressionBuilder;

import java.math.BigDecimal;

import java.sql.PreparedStatement;

import java.sql.SQLException;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import kz.asoft.am.common.RisksAppModule;
import kz.asoft.model.GRiskCalcsModelImpl;
import kz.asoft.model.GRiskValuesModelImpl;
import kz.asoft.model.GRisksCalcsObjModelImpl;
import kz.asoft.model.GRisksModelImpl;

import kz.asoft.view.GRiskValuesViewImpl;

import oracle.adf.share.ADFContext;

import oracle.jbo.JboException;
import oracle.jbo.Row;
import oracle.jbo.RowSet;
import oracle.jbo.RowSetIterator;
import oracle.jbo.Session;
import oracle.jbo.ViewObject;
import oracle.jbo.domain.Number;
import oracle.jbo.server.ApplicationModuleImpl;
import oracle.jbo.server.SequenceImpl;
import oracle.jbo.server.ViewLinkImpl;
import oracle.jbo.server.ViewObjectImpl;
import org.apache.log4j.Logger;

// ---------------------------------------------------------------------
// ---    File generated by Oracle ADF Business Components Design Time.
// ---    Sat Oct 05 15:23:31 UZT 2013
// ---    Custom code may be added to this class.
// ---    Warning: Do not modify method signatures of generated methods.
// ---------------------------------------------------------------------
public class RisksAppModuleImpl extends ApplicationModuleImpl implements RisksAppModule {
     private static Logger LOGGER = Logger.getLogger(RisksAppModuleImpl.class.getName());

    /**
     * This is the default constructor (do not remove).
     */
    public RisksAppModuleImpl() {
    }

    /**
     * Container's getter for SCtrlObjGroupsView1.
     * @return SCtrlObjGroupsView1
     */
    public ViewObjectImpl getSCtrlObjGroupsView1() {
        return (ViewObjectImpl)findViewObject("SCtrlObjGroupsView1");
    }

    /**
     * Container's getter for SCtrlObjsView1.
     * @return SCtrlObjsView1
     */
    public ViewObjectImpl getSCtrlObjsView1() {
        return (ViewObjectImpl)findViewObject("SCtrlObjsView1");
    }

    /**
     * Container's getter for GRisksCalcsObjView1.
     * @return GRisksCalcsObjView1
     */
    public ViewObjectImpl getGRisksCalcsObjView1() {
        return (ViewObjectImpl)findViewObject("GRisksCalcsObjView1");
    }

    /**
     * Container's getter for GRiskValuesView1.
     * @return GRiskValuesView1
     */
    public ViewObjectImpl getGRiskValuesView1() {
        return (ViewObjectImpl)findViewObject("GRiskValuesView1");
    }

    /**
     * Container's getter for SUnitsView1.
     * @return SUnitsView1
     */
    public ViewObjectImpl getSUnitsView1() {
        return (ViewObjectImpl)findViewObject("SUnitsView1");
    }

    /**
     * Container's getter for GRisksCurrentView1.
     * @return GRisksCurrentView1
     */
    public ViewObjectImpl getGRisksCurrentView1() {
        return (ViewObjectImpl)findViewObject("GRisksCurrentView1");
    }

    /**
     * Container's getter for SRiskValuesView1.
     * @return SRiskValuesView1
     */
    public ViewObjectImpl getSRiskValuesView1() {
        return (ViewObjectImpl)findViewObject("SRiskValuesView1");
    }

    /**
     * Container's getter for SCtrlObjsLink1.
     * @return SCtrlObjsLink1
     */
    public ViewLinkImpl getSCtrlObjsLink1() {
        return (ViewLinkImpl)findViewLink("SCtrlObjsLink1");
    }

    /**
     * Container's getter for CtrlObjRisksResultsLink1.
     * @return CtrlObjRisksResultsLink1
     */
    public ViewLinkImpl getCtrlObjRisksResultsLink1() {
        return (ViewLinkImpl)findViewLink("CtrlObjRisksResultsLink1");
    }

    /**
     * Container's getter for CtrlObjRisksResultValuesLink1.
     * @return CtrlObjRisksResultValuesLink1
     */
    public ViewLinkImpl getCtrlObjRisksResultValuesLink1() {
        return (ViewLinkImpl)findViewLink("CtrlObjRisksResultValuesLink1");
    }

    /**
     * Container's getter for RDataValue1Link1.
     * @return RDataValue1Link1
     */
    public ViewLinkImpl getRDataValue1Link1() {
        return (ViewLinkImpl)findViewLink("RDataValue1Link1");
    }

    /**
     * Container's getter for RDataValue2Link1.
     * @return RDataValue2Link1
     */
    public ViewLinkImpl getRDataValue2Link1() {
        return (ViewLinkImpl)findViewLink("RDataValue2Link1");
    }

    /**
     * Container's getter for RDataTypeLink1.
     * @return RDataTypeLink1
     */
    public ViewLinkImpl getRDataTypeLink1() {
        return (ViewLinkImpl)findViewLink("RDataTypeLink1");
    }

    /**
     * Container's getter for SUnitsLink1.
     * @return SUnitsLink1
     */
    public ViewLinkImpl getSUnitsLink1() {
        return (ViewLinkImpl)findViewLink("SUnitsLink1");
    }

    /**
     * Container's getter for GRisksCurrentLink1.
     * @return GRisksCurrentLink1
     */
    public ViewLinkImpl getGRisksCurrentLink1() {
        return (ViewLinkImpl)findViewLink("GRisksCurrentLink1");
    }

    public ViewObjectImpl getGRiskCalcsView1() {
        return (ViewObjectImpl)findViewObject("GRiskCalcsView1");
    }

    /**
     * Container's getter for SRisksValuesLink1.
     * @return SRisksValuesLink1
     */
    public ViewLinkImpl getSRisksValuesLink1() {
        return (ViewLinkImpl)findViewLink("SRisksValuesLink1");
    }

    /**
     * Container's getter for SRiskGroupValuesView1.
     * @return SRiskGroupValuesView1
     */
    public ViewObjectImpl getSRiskGroupValuesView1() {
        return (ViewObjectImpl)findViewObject("SRiskGroupValuesView1");
    }

    /**
     * Container's getter for SRiskListValuesView1.
     * @return SRiskListValuesView1
     */
    public ViewObjectImpl getSRiskListValuesView1() {
        return (ViewObjectImpl)findViewObject("SRiskListValuesView1");
    }

    /**
     * Container's getter for SRiskListValuesLink1.
     * @return SRiskListValuesLink1
     */
    public ViewLinkImpl getSRiskListValuesLink1() {
        return (ViewLinkImpl)findViewLink("SRiskListValuesLink1");
    }

    /**
     * Container's getter for SRiskListValuesView2.
     * @return SRiskListValuesView2
     */
    public ViewObjectImpl getSRiskListValuesView2() {
        return (ViewObjectImpl)findViewObject("SRiskListValuesView2");
    }

    /**
     * Container's getter for SRiskValuesView2.
     * @return SRiskValuesView2
     */
    public ViewObjectImpl getSRiskValuesView2() {
        return (ViewObjectImpl)findViewObject("SRiskValuesView2");
    }

    /**
     * Container's getter for SRiskGroupsView1.
     * @return SRiskGroupsView1
     */
    public ViewObjectImpl getSRiskGroupsView1() {
        return (ViewObjectImpl)findViewObject("SRiskGroupsView1");
    }

    /**
     * Container's getter for GRisksListView1.
     * @return GRisksListView1
     */
    public ViewObjectImpl getGRisksListView1() {
        return (ViewObjectImpl)findViewObject("GRisksListView1");
    }

    /**
     * Container's getter for RDataPeriodView1.
     * @return RDataPeriodView1
     */
    public ViewObjectImpl getRDataPeriodView1() {
        return (ViewObjectImpl)findViewObject("RDataPeriodView1");
    }

    /**
     * Container's getter for RDataValueByFormulaView1.
     * @return RDataValueByFormulaView1
     */
    public ViewObjectImpl getRDataValueByFormulaView1() {
        return (ViewObjectImpl)findViewObject("RDataValueByFormulaView1");
    }

    /**
     * Container's getter for CtrlObjDataPeriodViewLink1.
     * @return CtrlObjDataPeriodViewLink1
     */
    public ViewLinkImpl getCtrlObjDataPeriodViewLink1() {
        return (ViewLinkImpl)findViewLink("CtrlObjDataPeriodViewLink1");
    }
    
    /**
     * Container's getter for SRiskMaxValueView1.
     * @return SRiskMaxValueView1
     */
    public ViewObjectImpl getSRiskMaxValueView1() {
        return (ViewObjectImpl)findViewObject("SRiskMaxValueView1");
    }

    /**
     * Формирование БД для анализа рисков, анализ рисков
     */
    public void createFormData() {
        //Получение анализов рисков за последние 2 года
        Number ctrlObjId =
            (Number)getSCtrlObjsView1().getCurrentRow().getAttribute("Id");
        int year = Calendar.getInstance().get(Calendar.YEAR);
        ViewObject gRiskCalcVO =
            getCtrlObjRisksResultsLink1().getDestination();
        gRiskCalcVO.setWhereClause(" (YEAR BETWEEN " + (year - 2) + " AND " +
                                   year + ") ");
        gRiskCalcVO.executeQuery();
        Row lastRiskCalc = gRiskCalcVO.last();

        //Период анализа рисков
        Number riskCalcId = getRiskCalcsNextSeq();
        Row newRiskCalcsRow = getGRiskCalcsView1().createRow();
        newRiskCalcsRow.setAttribute("Id", riskCalcId);
        newRiskCalcsRow.setAttribute("Year", year);
        newRiskCalcsRow.setAttribute("OpenDate", new Date());

        //Результат анализа риска
        Number riskCalcObjId = getRiskCalcsObjNextSeq();
        ViewObject riskCalcsObjVO = getGRisksCalcsObjView1();
        Row newRiskCalcsObjRow = riskCalcsObjVO.createRow();
        newRiskCalcsObjRow.setAttribute("Id", riskCalcObjId);
        newRiskCalcsObjRow.setAttribute("RcyId", riskCalcId);
        newRiskCalcsObjRow.setAttribute("CtrlObjId", ctrlObjId);

        RowSetIterator rsRisksIterator =
            getGRisksCurrentLink1().getDestination().createRowSetIterator(null);

        Double resultRiskScoring = 0.0;
        Double riskValue = 0.0;
        Integer riskListValue = 0;
        double totalScoring = rsRisksIterator.getRowCount() * 5;

        while (rsRisksIterator.hasNext()) {
            Row riskRow = rsRisksIterator.next();
            RowSet riskValuesRS =
                (RowSet)riskRow.getAttribute("SRiskValuesView");
            riskValue = (riskRow.getAttribute("RiskValue") != null ? Double.valueOf(riskRow.getAttribute("RiskValue").toString()) : null);
            riskListValue = (riskRow.getAttribute("RiskValueId") != null ? Integer.valueOf(riskRow.getAttribute("RiskValueId").toString()) : null);
            if (riskValuesRS.getRowCount() > 0) {
                Row newRiskValuesRow = getGRiskValuesView1().createRow();
                Number gRiskValueId = getGRiskValuesNextSeq();
                newRiskValuesRow.setAttribute("Id", gRiskValueId);
                newRiskValuesRow.setAttribute("RiskClacId", riskCalcObjId);
                newRiskValuesRow.setAttribute("RiskValue", riskValue);
                //Установка риска, группы риска по введённому показателю
                Map<String, Object> riskMap =
                    getCalculatedValues((Number)riskRow.getAttribute("Id"), riskValuesRS, riskValue,
                                        riskListValue);
                newRiskValuesRow.setAttribute("RiskValueId",
                                              riskMap.get("id"));
                newRiskValuesRow.setAttribute("RiskGroupId",
                                              riskMap.get("groupId"));
                //Подсчёт процентной доли
                resultRiskScoring +=
                        Double.valueOf(riskMap.get("code").toString());
            }
        }
        //Установка группы риска объекту контроля
        getSRiskGroupValuesView1().executeQuery();
        RowSet riskGroupValuesRS = getSRiskGroupValuesView1().getRowSet();
        newRiskCalcsObjRow.setAttribute("TotalScore", totalScoring);
        newRiskCalcsObjRow.setAttribute("ResultScore", resultRiskScoring);
        resultRiskScoring = resultRiskScoring / totalScoring * 100;
        newRiskCalcsObjRow.setAttribute("Percentage", resultRiskScoring);
        Map<String, Object> riskMap =
            getCalculatedValues(null, riskGroupValuesRS, resultRiskScoring,
                                null);
        //Проверка наличия анализов рисков за последние 2 года
        if (lastRiskCalc == null) {
            newRiskCalcsObjRow.setAttribute("ResultRiskGroupId",
                                            riskMap.get("groupId"));
        } else {
            Row row = riskGroupValuesRS.first();
            newRiskCalcsObjRow.setAttribute("ResultRiskGroupId",
                                            row.getAttribute("RiskGroupId"));
        }

        getTransaction().commit();
        riskCalcsObjVO.executeQuery();
    }

    /**
     * Получение расчитанного значения риска, группы риска, значения кода для подсчёта процентной доли
     * @param gRiskValueId - Risk ID
     * @param rowSet - RowSet of risk values, group values
     * @param compareValue - Input risk value
     * @param compareIdValue - Selected risk list value
     * @return
     */
    private Map<String, Object> getCalculatedValues(Number gRiskValueId,
                                                    RowSet rowSet,
                                                    Double compareValue,
                                                    Integer compareIdValue) {
        Map<String, Object> map = new HashMap<String, Object>();
        //Если введённый показатель равен NULL
        if (compareValue == null && compareIdValue == null) {
            ViewObject maxValueVO = getSRiskMaxValueView1();
            maxValueVO.setWhereClause(" RISK_ID = " + gRiskValueId + " ");
            maxValueVO.executeQuery();
            Row row = maxValueVO.getRowSet().first();
            map.put("id", row.getAttribute("Id"));
            map.put("groupId", row.getAttribute("RiskGroupId"));
            map.put("code", row.getAttribute("Code"));
        } else {
            while (rowSet.hasNext()) {
                Row row = rowSet.next();
                double value1 =
                    Double.valueOf(row.getAttribute("Value1") == null ? "0" :
                                   row.getAttribute("Value1").toString());
                double value2 =
                    Double.valueOf(row.getAttribute("Value2") == null ? "0" :
                                   row.getAttribute("Value2").toString());
                int oper =
                    Integer.valueOf(row.getAttribute("Oper") == null ? "-1" :
                                    row.getAttribute("Oper").toString());
                //Установка риска, группы риска, кода
                map.put("id", row.getAttribute("Id"));
                map.put("groupId", row.getAttribute("RiskGroupId"));
                map.put("code", row.getAttribute("Code"));

                if (oper < 0) {
                    int riskListId =
                        Integer.valueOf(row.getAttribute("RiskListValueId").toString());
                    if (compareIdValue == riskListId) {
                        break;
                    }
                } else if (oper == 0) {
                    if (compareValue > value1) {
                        break;
                    }
                } else if (oper == 1) {
                    if (compareValue < value1) {
                        break;
                    }
                } else if (oper == 2) {
                    if (compareValue <= value1) {
                        break;
                    }
                } else if (oper == 3) {
                    if (compareValue >= value1) {
                        break;
                    }
                } else if (oper == 4) {
                    if (compareValue == value1) {
                        break;
                    }
                } else if (oper == 5) {
                    if (compareValue != value1) {
                        break;
                    }
                } else if (oper == 6) {
                    if (compareValue >= value1 && compareValue <= value2) {
                        break;
                    }
                }
            }
        }
        return map;
    }

    /**
     * Поиск по наименованию объекта контроля
     * @param searchText
     */
    public void searchCtrlObj(String searchText) {
        ViewObject searchVO = getSCtrlObjsView1();
        searchVO.setWhereClause("(UPPER(NAME) LIKE UPPER('%" + searchText +
                                "%'))");
        searchVO.executeQuery();
    }

    /**
     * Посдчёт показателей квазигос-ых субъектов
     */
    public void setRiskValuesByFormula() {
        RowSetIterator rsRisksIterator =
            getGRisksCurrentLink1().getDestination().createRowSetIterator(null);
        while (rsRisksIterator.hasNext()) {
            Row riskRow = rsRisksIterator.next();
            Number result =
                getCalculatedValueByFormula(riskRow.getAttribute("Formula") !=
                                            null ?
                                            riskRow.getAttribute("Formula").toString() :
                                            null);
            riskRow.setAttribute("RiskValue", result);
        }
        ViewObject rDataPeriodVO = getRDataPeriodView1();
        rDataPeriodVO.setWhereClause(null);
        rDataPeriodVO.executeQuery();
    }

    /**
     * Получение значения по формуле
     * @param formula
     * @return
     */
    private Number getCalculatedValueByFormula(String formula) {
        if (formula == null) {
            return null;
        }
        try {
            StringBuilder ids = new StringBuilder();

            //Получение IDS значений информации квазигос-ых субъектов
            Pattern r = Pattern.compile("\\[\\d+");
            Matcher m = r.matcher(formula);
            String comma = "";
            while (m.find()) {
                ids.append(comma).append(m.group().substring(1,
                                                             m.group().length()));
                comma = ",";
            }

            //Получение значений информации квазигос-ых субъектов по IDS
            ViewObject rDataPeriodVO = getRDataPeriodView1();
            rDataPeriodVO.setWhereClause(" CURRENT_DATE BETWEEN START_DATE AND END_DATE ");
            rDataPeriodVO.setOrderByClause("ID");
            rDataPeriodVO.executeQuery();
            if (rDataPeriodVO.getRowCount() == 0) {
                return null;
            }
            Row rDataPeriodRow = rDataPeriodVO.last();
            ViewObject rDataValueVO = getRDataValueByFormulaView1();
            rDataValueVO.setWhereClause(" DATA_ID IN (" + ids +
                                        ") AND PERIOD_ID = " +
                                        rDataPeriodRow.getAttribute("Id"));
            rDataValueVO.executeQuery();

            //Формирование list из блоков формулы
            Pattern pattern = Pattern.compile("\\[\\d+\\,\\s*\\d+\\]");
            Matcher m2 = pattern.matcher(formula);
            List<String> list = new ArrayList<String>();
            while (m2.find()) {
                list.add(m2.group());
            }

            //Формирование map со значениями квазигос-ых субъектов
            Map<String, BigDecimal> map = new HashMap<String, BigDecimal>();
            for (String s : list) {
                String[] strs = s.split(",");
                String param1 = strs[0].substring(1, strs[0].length()).trim();
                String param2 =
                    strs[1].substring(0, strs[1].length() - 1).trim();

                RowSetIterator iterator =
                    rDataValueVO.createRowSetIterator(null);
                while (iterator.hasNext()) {
                    Row row = iterator.next();
                    if (row.getAttribute("DataId").toString().equals(param1)) {
                        map.put(s,
                                new BigDecimal(param2.equals("1") ? row.getAttribute("BeginValue").toString() :
                                               row.getAttribute("EndValue").toString()));
                    }
                }
            }

            //Формирование формулы для расчёта значения
            for (String key : map.keySet()) {
                formula =
                        formula.replaceAll(key.replace("]", "\\]").replace("[",
                                                                           "\\["),
                                           map.get(key).toString());
            }

            //Валидация формулы
            if (formula.contains("[")) {
                return null;
            }
            //Расчёт значения
            Calculable calc = new ExpressionBuilder(formula).build();
            return new Number(calc.calculate());
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Получение списка значений рисков(текстовые значения)
     * @param riskId
     */
    public void getRiskListValuesByRiskId(Number riskId) {
        ViewObject riskListValuesVO = getSRiskListValuesView2();
        riskListValuesVO.setWhereClause(" RISK_ID = " + riskId + "");
        riskListValuesVO.executeQuery();
    }

    /**
     * Get sequence by name
     * @param seqName
     * @return
     */
    private Number getSequenceId(String seqName) {
        SequenceImpl s = new SequenceImpl(seqName, getDBTransaction());
        return s.getSequenceNumber();
    }

    /**
     * Get risk calcs next sequence
     * @return
     */
    private Number getRiskCalcsNextSeq() {
        return getSequenceId(GRiskCalcsModelImpl.G_RISK_CALCS_SEQ);
    }

    /**
     * Get risk calcs object next sequence
     * @return
     */
    private Number getRiskCalcsObjNextSeq() {
        return getSequenceId(GRisksCalcsObjModelImpl.G_RISKS_CALCS_OBJ_SEQ);
    }

    /**
     * Get GRisk values next sequence
     * @return
     */
    private Number getGRiskValuesNextSeq() {
        return getSequenceId(GRiskValuesModelImpl.G_RISK_VALUES_SEQ);
    }

    /**
     * Get risks next sequence
     * @return
     */
    private Number getRiskNextSeq() {
        return getSequenceId(GRisksModelImpl.RISK_SEC_NAME);
    }

    protected void prepareSession(Session session) {
        super.prepareSession(session);
        // do session-specific initializations
        this.callStroredProcedure();
    }

    protected void callStroredProcedure() {
           LOGGER.debug("START");
        PreparedStatement st = null;
        int updatedRow = 0;
        try {
            String stmt =
                "begin pkg_audit.setUserName('" + this.getUserName() +
                "'); end;";
            st = getDBTransaction().createPreparedStatement(stmt, 0);
            updatedRow = st.executeUpdate();
        } catch (SQLException e) {
            LOGGER.error(e);
            throw new JboException(e);
        } catch (Exception e) {
            LOGGER.error(e);
        } finally {
            if (st != null) {
                try {
                    st.close();
                } catch (SQLException e) {
                    LOGGER.error(e);
                    e.printStackTrace();
                }
            }
        }
            LOGGER.debug("FINISH");
    }

    public static String getUserName() {
            LOGGER.debug("START");
        ADFContext adfContext = ADFContext.getCurrent();
        String userName = adfContext.getSecurityContext().getUserName();
            LOGGER.debug("userName - " + userName);
        return userName;
        //        LOGGER.debug("FINISH");
    }
}
